#include "actioncommands.h"

#include "global.h"

/************************************************************/
/*                   ACTION COMMAND CLASS                   */
/************************************************************/
ActionCommand::ActionCommand(RobotAction *action):
    _action(action)
{}


/************************************************************/
/*               ACTION COMMAND GROUP CLASS                 */
/************************************************************/
ActionCommandGroup::ActionCommandGroup(RobotAction *action):
    ActionCommand(action)
{}

void ActionCommandGroup::execute(){
    _action->startBatch();

    //Iterate through the the command list and execute them
    for(int i = 0; i < _commandList.length(); i++){
        _commandList.at(i)->execute();
    }

    _action->endBatch();
}

void ActionCommandGroup::undo(){
    _action->startBatch();

    for(int i = _commandList.length() - 1; i >= 0; i--){
        _commandList.at(i)->undo();
    }

    _action->endBatch();
}

void ActionCommandGroup::append(ActionCommand *command){
    _commandList.append(command);
}


/************************************************************/
/*              INSERT KEY FRAME COMMAND CLASS              */
/************************************************************/
InsertKeyFrameCommand::InsertKeyFrameCommand(RobotAction* action, int msec):
    ActionCommand(action),
    _timestamp(msec),
    _jointID(-1),
    _joint(false)
{}

InsertKeyFrameCommand::InsertKeyFrameCommand(RobotAction* action, int msec, int jointID):
    ActionCommand(action),
    _timestamp(msec),
    _jointID(jointID),
    _joint(true)
{}

void InsertKeyFrameCommand::execute(){
    if(_joint)
        _insertedValue = _action->insertKeyFrameAt(_timestamp, _jointID);
    else
        _insertedFrame = _action->insertKeyFrameAt(_timestamp);
}

void InsertKeyFrameCommand::undo(){
    if(_joint){
        if(_insertedValue != INVALID_KEYFRAME)
            //The insert was valid and created a key frame, we need to delete it.
            //Otherwise, the insert did nothing and we don't have anything to undo.
            _action->deleteKeyFrameAt(_timestamp, _jointID);
    }
    else{
        //Cycle through the inserted key frame that was returned.
        //If the value of the joint is not INVALID_KEYFRAME, then we need to delete
        //the key frame for that joint that was created, otherwise do nothing.
        int i;
        for(i = 0; i < _insertedFrame.jointValues.count(); i++){
            if(_insertedFrame.jointValues[i].second != INVALID_KEYFRAME){
                _action->deleteKeyFrameAt(_timestamp, _insertedFrame.jointValues[i].first);
            }
        }
    }
}


/************************************************************/
/*              DELETE KEY FRAME COMMAND CLASS              */
/************************************************************/
DeleteKeyFrameCommand::DeleteKeyFrameCommand(RobotAction *action, int msec):
    ActionCommand(action),
    _timestamp(msec),
    _jointID(-1),
    _joint(false)
{}

DeleteKeyFrameCommand::DeleteKeyFrameCommand(RobotAction *action, int msec, int jointID):
    ActionCommand(action),
    _timestamp(msec),
    _jointID(jointID),
    _joint(true)
{}

void DeleteKeyFrameCommand::execute(){
    if(_joint)
        _deletedValue = _action->deleteKeyFrameAt(_timestamp, _jointID);
    else
        _deletedFrame = _action->deleteKeyFrameAt(_timestamp);
}

void DeleteKeyFrameCommand::undo(){
    if(_joint){
        //Restore the deleted value
        _action->setKeyFrameAt(_timestamp, _jointID, _deletedValue);
    }
    else{
        //Restore the deleted frame
        int i;
        for(i = 0; i < _deletedFrame.jointValues.count(); i++){
            _action->setKeyFrameAt(_timestamp, _deletedFrame.jointValues[i].first,
                                   _deletedFrame.jointValues[i].second);
        }
    }
}


/************************************************************/
/*               MOVE KEY FRAME COMMAND CLASS               */
/************************************************************/
MoveKeyFrameCommand::MoveKeyFrameCommand(RobotAction *action, int msec, int newMsec):
    ActionCommand(action),
    _timestamp(msec),
    _newTimestamp(newMsec),
    _jointID(-1),
    _joint(false)
{}

MoveKeyFrameCommand::MoveKeyFrameCommand(RobotAction *action, int msec, int jointID, int newMsec):
    ActionCommand(action),
    _timestamp(msec),
    _newTimestamp(newMsec),
    _jointID(jointID),
    _joint(true)
{}


void MoveKeyFrameCommand::execute(){
    if(_joint)
        _action->moveKeyFrame(_timestamp, _jointID, _newTimestamp);
    else
        _action->moveKeyFrame(_timestamp, _newTimestamp);
}

void MoveKeyFrameCommand::undo(){
    if(_joint)
        _action->moveKeyFrame(_newTimestamp, _jointID, _timestamp);
    else
        _action->moveKeyFrame(_newTimestamp, _timestamp);
}


/************************************************************/
/*                SET KEY FRAME COMMAND CLASS               */
/************************************************************/
SetKeyFrameCommand::SetKeyFrameCommand(RobotAction *action, int msec, int jointID, float theta):
    ActionCommand(action),
    _timestamp(msec),
    _jointID(jointID),
    _theta(theta)
{}

void SetKeyFrameCommand::execute(){
    _prevTheta = _action->keyValueAt(_timestamp, _jointID);
    _action->setKeyFrameAt(_timestamp, _jointID, _theta);
}

void SetKeyFrameCommand::undo(){
    if(_prevTheta == INVALID_KEYFRAME){
        //A key frame was created, so we need to delete it
        _action->deleteKeyFrameAt(_timestamp, _jointID);
    }
    else{
        //The angle was set; reset it
        _action->setKeyFrameAt(_timestamp, _jointID, _prevTheta);
    }
}


/************************************************************/
/*                  SET SOUND COMMAND CLASS                 */
/************************************************************/
SetSoundCommand::SetSoundCommand(RobotAction *action, int msec, QString filename):
    ActionCommand(action),
    _timestamp(msec),
    _filename(filename)
{}

void SetSoundCommand::execute(){
    _prevSound = _action->soundAt(_timestamp);
    _action->setSoundAt(_timestamp, _filename);
}

void SetSoundCommand::undo(){
    if(_prevSound.timestamp == -1){
        //There was no sound at the time, a new one was created
        _action->deleteSound(_timestamp);
    }
    else{
        _action->setSoundAt(_timestamp, *_prevSound.filename);
    }
}


/************************************************************/
/*                DELETE SOUND COMMAND CLASS                */
/************************************************************/
DeleteSoundCommand::DeleteSoundCommand(RobotAction *action, int msec):
    ActionCommand(action),
    _timestamp(msec)
{}

void DeleteSoundCommand::execute(){
    _deletedSound = _action->deleteSound(_timestamp);
}

void DeleteSoundCommand::undo(){
    _action->setSoundAt(_deletedSound.timestamp, *_deletedSound.filename);
    //TODO: clip to the duration if we ever add that functionality
}


/************************************************************/
/*                 MOVE SOUND COMMAND CLASS                 */
/************************************************************/
MoveSoundCommand::MoveSoundCommand(RobotAction *action, int msec, int newMsec):
    ActionCommand(action),
    _timestamp(msec),
    _newTimestamp(newMsec)
{}

void MoveSoundCommand::execute(){
    _action->moveSound(_timestamp, _newTimestamp);
}

void MoveSoundCommand::undo(){
    _action->moveSound(_newTimestamp, _timestamp);
}


/************************************************************/
/*                 TRIM SOUND COMMAND CLASS                 */
/************************************************************/
TrimSoundCommand::TrimSoundCommand(RobotAction *action, int msec, int frontTrim, int backTrim):
    ActionCommand(action),
    _timestamp(msec),
    _frontTrim(frontTrim),
    _backTrim(backTrim)
{}

void TrimSoundCommand::execute(){
    //The trim command may change the timestamp of the sound.
    _timestamp = _action->trimSound(_timestamp, _frontTrim, _backTrim);
}

void TrimSoundCommand::undo(){
    _timestamp = _action->trimSound(_timestamp, -_frontTrim, -_backTrim);
}
